
/**
  ******************************************************************************
  * Copyright 2021 The Microbee Authors. All Rights Reserved.
  * 
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
  * 
  * http://www.apache.org/licenses/LICENSE-2.0
  * 
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  * See the License for the specific language governing permissions and
  * limitations under the License.
  * 
  * @file       battery_monitor_backend.h
  * @author     baiyang
  * @date       2022-10-26
  ******************************************************************************
  */

#pragma once

#ifdef __cplusplus
extern "C"{
#endif

/*----------------------------------include-----------------------------------*/
#include "battery_monitor_params.h"

#include <rtthread.h>

#include <stdint.h>
#include <stdbool.h>

#include <parameter/param.h>
#include <common/gp_math/gp_mathlib.h>
/*-----------------------------------macro------------------------------------*/
// maximum number of battery monitors
#ifndef MB_BATT_MONITOR_MAX_INSTANCES
#define MB_BATT_MONITOR_MAX_INSTANCES       3
#endif

// first monitor is always the primary monitor
#define MB_BATT_PRIMARY_INSTANCE            0

#define MB_BATT_SERIAL_NUMBER_DEFAULT       -1

#define MB_BATT_MONITOR_TIMEOUT             5000

#define MB_BATT_MONITOR_RES_EST_TC_1        0.5f
#define MB_BATT_MONITOR_RES_EST_TC_2        0.1f

#if !defined(HAL_MINIMIZE_FEATURES) && BOARD_FLASH_SIZE > 1024
#define MB_BATT_MONITOR_CELLS_MAX           14
#else
#define MB_BATT_MONITOR_CELLS_MAX           12
#endif

#ifndef MB_BATTMON_SMBUS_ENABLE
#define MB_BATTMON_SMBUS_ENABLE 1
#endif

#ifndef MB_BATTMON_FUELFLOW_ENABLE
#define MB_BATTMON_FUELFLOW_ENABLE (BOARD_FLASH_SIZE > 1024)
#endif

#ifndef MB_BATTMON_FUELLEVEL_PWM_ENABLE
#define MB_BATTMON_FUELLEVEL_PWM_ENABLE (BOARD_FLASH_SIZE > 1024)
#endif

#ifndef MB_BATTMON_FUELLEVEL_ANALOG_ENABLE
#define MB_BATTMON_FUELLEVEL_ANALOG_ENABLE (BOARD_FLASH_SIZE > 1024)
#endif

/*----------------------------------typedef-----------------------------------*/
// battery failsafes must be defined in levels of severity so that vehicles wont fall backwards
enum BattMonitorFailsafe {
    BattMonitorFailsafeNone = 0,
    BattMonitorFailsafeLow,
    BattMonitorFailsafeCritical
};

// Battery monitor driver types
enum BattMonitorType {
    BATT_MONITOR_NONE                       = 0,
    BATT_MONITOR_ANALOG_VOLTAGE_ONLY        = 3,
    BATT_MONITOR_ANALOG_VOLTAGE_AND_CURRENT = 4,
    BATT_MONITOR_SOLO                       = 5,
    BATT_MONITOR_BEBOP                      = 6,
    BATT_MONITOR_SMBus_Generic              = 7,
    BATT_MONITOR_UAVCAN_BatteryInfo         = 8,
    BATT_MONITOR_BLHeliESC                  = 9,
    BATT_MONITOR_Sum                        = 10,
    BATT_MONITOR_FuelFlow                   = 11,
    BATT_MONITOR_FuelLevel_PWM              = 12,
    BATT_MONITOR_SUI3                       = 13,
    BATT_MONITOR_SUI6                       = 14,
    BATT_MONITOR_NeoDesign                  = 15,
    BATT_MONITOR_MAXELL                     = 16,
    BATT_MONITOR_GENERATOR_ELEC             = 17,
    BATT_MONITOR_GENERATOR_FUEL             = 18,
    BATT_MONITOR_Rotoye                     = 19,
    // 20 was MPPT_PacketDigital
    BATT_MONITOR_INA2XX                     = 21,
    BATT_MONITOR_LTC2946                    = 22,
    BATT_MONITOR_Torqeedo                   = 23,
    BATT_MONITOR_FuelLevel_Analog           = 24,
};

// cell voltages in millivolts
struct BattMonitor_Cells {
    uint16_t cells[MB_BATT_MONITOR_CELLS_MAX];
};

typedef struct BattMonitor_State* battery_monitor_state_t;

// The BattMonitor_State structure is filled in by the backend driver
struct BattMonitor_State {
    struct BattMonitor_Cells    cell_voltages;             // battery cell voltages in millivolts, 10 cells matches the MAVLink spec
    float                       voltage;                   // voltage in volts
    float                       current_amps;              // current in amperes
    float                       consumed_mah;              // total current draw in milliamp hours since start-up
    float                       consumed_wh;               // total energy consumed in Wh since start-up
    uint32_t                    last_time_micros;          // time when voltage and current was last read in microseconds
    uint32_t                    low_voltage_start_ms;      // time when voltage dropped below the minimum in milliseconds
    uint32_t                    critical_voltage_start_ms; // critical voltage failsafe start timer in milliseconds
    float                       temperature;               // battery temperature in degrees Celsius
#if MB_TEMPERATURE_SENSOR_ENABLED
    bool                        temperature_external_use;
    float                       temperature_external;      // battery temperature set by an external source in degrees Celsius
#endif
    uint32_t                    temperature_time;          // timestamp of the last received temperature message
    float                       voltage_resting_estimate;  // voltage with sag removed based on current and resistance estimate in Volt
    float                       resistance;                // resistance, in Ohms, calculated by comparing resting voltage vs in flight voltage
    enum BattMonitorFailsafe    failsafe;                     // stage failsafe the battery is in
    bool                        healthy;                   // battery monitor is communicating correctly
    bool                        is_powering_off;           // true when power button commands power off
    bool                        powerOffNotified;          // only send powering off notification once
    uint32_t                    time_remaining;            // remaining battery time
    bool                        has_time_remaining;        // time_remaining is only valid if this is true
};

typedef struct battery_monitor_backend* battery_monitor_backend_t;
typedef struct battery_monitor_backend_ops* battery_monitor_backend_ops_t;

struct battery_monitor_backend {
    battery_monitor_backend_ops_t ops;

    battery_monitor_state_t      _state;    // reference to this instances state (held in the front-end)
    battery_monitor_params_t     _params;   // reference to this instances parameters (held in the front-end)

    // resistance estimate
    uint32_t                     _resistance_timer_ms;    // system time of last resistance estimate update
    float                        _voltage_filt;           // filtered voltage
    float                        _current_max_amps;       // maximum current since start-up
    float                        _current_filt_amps;      // filtered current
    float                        _resistance_voltage_ref; // voltage used for maximum resistance calculation
    float                        _resistance_current_ref; // current used for maximum resistance calculation
};

struct battery_monitor_backend_ops {
    // we declare a virtual destructor so that BattMonitor driver can
    // override with a custom destructor if need be
    void (*battery_monitor_backend_destructor)(battery_monitor_backend_t battmonitor_backend);

    // initialise
    void (*init)(battery_monitor_backend_t battmonitor_backend);

    // read the latest battery voltage
    void (*read)(battery_monitor_backend_t battmonitor_backend);

    /// returns true if battery monitor instance provides time remaining info
    bool (*has_time_remaining)(battery_monitor_backend_t battmonitor_backend);

    /// returns true if battery monitor instance provides consumed energy info
    bool (*has_consumed_energy)(battery_monitor_backend_t battmonitor_backend);

    /// returns true if battery monitor instance provides current info
    bool (*has_current)(battery_monitor_backend_t battmonitor_backend);

    // returns true if battery monitor provides individual cell voltages
    bool (*has_cell_voltages)(battery_monitor_backend_t battmonitor_backend);

    // returns true if battery monitor provides temperature
    bool (*has_temperature)(battery_monitor_backend_t battmonitor_backend);

    // capacity_remaining_pct - returns true if the battery % is available and writes to the percentage argument
    // returns false if the battery is unhealthy, does not have current monitoring, or the pack_capacity is too small
    bool (*capacity_remaining_pct)(battery_monitor_backend_t battmonitor_backend, uint8_t *percentage);

    // return true if cycle count can be provided and fills in cycles argument
    bool (*get_cycle_count)(battery_monitor_backend_t battmonitor_backend, uint16_t *cycles);

    // updates failsafe timers, and returns what failsafes are active
    enum BattMonitorFailsafe (*update_failsafes)(battery_monitor_backend_t battmonitor_backend);

    // reset remaining percentage to given value
    bool (*reset_remaining)(battery_monitor_backend_t battmonitor_backend, float percentage);

    // return mavlink fault bitmask (see MAV_BATTERY_FAULT enum)
    uint32_t (*get_mavlink_fault_bitmask)(battery_monitor_backend_t battmonitor_backend);
};

/*----------------------------------variable----------------------------------*/

/*-------------------------------------os-------------------------------------*/

/*----------------------------------function----------------------------------*/
// we declare a virtual destructor so that BattMonitor driver can
// override with a custom destructor if need be
void battmonitor_backend_ctor(battery_monitor_backend_t battmonitor_backend,
                                           battery_monitor_backend_ops_t ops,
                                           battery_monitor_state_t mon_state,
                                           battery_monitor_params_t params);

/// get voltage with sag removed (based on battery current draw and resistance)
/// this will always be greater than or equal to the raw voltage
float battmonitor_backend_voltage_resting_estimate(battery_monitor_backend_t battmonitor_backend);

// update battery resistance estimate and voltage_resting_estimate
void battmonitor_backend_update_resistance_estimate(battery_monitor_backend_t battmonitor_backend);

// returns false if we fail arming checks, in which case the buffer will be populated with a failure message
bool battmonitor_backend_arming_checks(battery_monitor_backend_t battmonitor_backend, char * buffer, size_t buflen);

// checks what failsafes could be triggered
void battmonitor_backend_check_failsafe_types(battery_monitor_backend_t battmonitor_backend,
                                                                 bool *low_voltage, bool *low_capacity,
                                                                 bool *critical_voltage,
                                                                 bool *critical_capacity);
void battmonitor_backend_update_consumed(battery_monitor_state_t state, uint32_t dt_us);

// logging functions 
void battmonitor_backend_log_write_bat(battery_monitor_backend_t battmonitor_backend, const uint8_t instance, const uint64_t time_us);
void battmonitor_backend_log_write_bcl(battery_monitor_backend_t battmonitor_backend, const uint8_t instance, const uint64_t time_us);

// we declare a virtual destructor so that BattMonitor driver can
// override with a custom destructor if need be
static inline void battmonitor_backend_destructor(battery_monitor_backend_t battmonitor_backend) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->battery_monitor_backend_destructor != NULL) {
        battmonitor_backend->ops->battery_monitor_backend_destructor(battmonitor_backend);
    }
}

// initialise
static inline void battmonitor_backend_init(battery_monitor_backend_t battmonitor_backend) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->init != NULL) {
        battmonitor_backend->ops->init(battmonitor_backend);
    }
}

// read the latest battery voltage
static inline void battmonitor_backend_read(battery_monitor_backend_t battmonitor_backend) {
    RT_ASSERT(battmonitor_backend != RT_NULL);
    RT_ASSERT(battmonitor_backend->ops != RT_NULL);
    RT_ASSERT(battmonitor_backend->ops->read != RT_NULL);

    battmonitor_backend->ops->read(battmonitor_backend);
}

/// returns true if battery monitor instance provides time remaining info
static inline bool battmonitor_backend_has_time_remaining(battery_monitor_backend_t battmonitor_backend) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->has_time_remaining != NULL) {
        return battmonitor_backend->ops->has_time_remaining(battmonitor_backend);
    }

    return false;
}

/// returns true if battery monitor instance provides consumed energy info
static inline bool battmonitor_backend_has_consumed_energy(battery_monitor_backend_t battmonitor_backend) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->has_consumed_energy != NULL) {
        return battmonitor_backend->ops->has_consumed_energy(battmonitor_backend);
    }

    return false;
}

/// returns true if battery monitor instance provides current info
static inline bool battmonitor_backend_has_current(battery_monitor_backend_t battmonitor_backend) {
    RT_ASSERT(battmonitor_backend != RT_NULL);
    RT_ASSERT(battmonitor_backend->ops != RT_NULL);
    RT_ASSERT(battmonitor_backend->ops->has_current != RT_NULL);

    return battmonitor_backend->ops->has_current(battmonitor_backend);
}

// returns true if battery monitor provides individual cell voltages
static inline bool battmonitor_backend_has_cell_voltages(battery_monitor_backend_t battmonitor_backend) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->has_cell_voltages != NULL) {
        return battmonitor_backend->ops->has_cell_voltages(battmonitor_backend);
    }

    return false;
}

// returns true if battery monitor provides temperature
static inline bool battmonitor_backend_has_temperature(battery_monitor_backend_t battmonitor_backend) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->has_temperature != NULL) {
        return battmonitor_backend->ops->has_temperature(battmonitor_backend);
    }

    return false;
}

// capacity_remaining_pct - returns true if the battery % is available and writes to the percentage argument
// returns false if the battery is unhealthy, does not have current monitoring, or the pack_capacity is too small
static inline bool battmonitor_backend_capacity_remaining_pct(battery_monitor_backend_t battmonitor_backend, uint8_t *percentage) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->capacity_remaining_pct != NULL) {
        return battmonitor_backend->ops->capacity_remaining_pct(battmonitor_backend, percentage);
    }

    return false;
}

// return true if cycle count can be provided and fills in cycles argument
static inline bool battmonitor_backend_get_cycle_count(battery_monitor_backend_t battmonitor_backend, uint16_t *cycles) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->get_cycle_count != NULL) {
        return battmonitor_backend->ops->get_cycle_count(battmonitor_backend, cycles);
    }

    return false;
}

// updates failsafe timers, and returns what failsafes are active
static inline enum BattMonitorFailsafe battmonitor_backend_update_failsafes(battery_monitor_backend_t battmonitor_backend) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->update_failsafes != NULL) {
        return battmonitor_backend->ops->update_failsafes(battmonitor_backend);
    }

    return false;
}

// reset remaining percentage to given value
static inline bool battmonitor_backend_reset_remaining(battery_monitor_backend_t battmonitor_backend, float percentage) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->reset_remaining != NULL) {
        return battmonitor_backend->ops->reset_remaining(battmonitor_backend, percentage);
    }

    return false;
}

// return mavlink fault bitmask (see MAV_BATTERY_FAULT enum)
static inline uint32_t battmonitor_backend_get_mavlink_fault_bitmask(battery_monitor_backend_t battmonitor_backend) {
    if (battmonitor_backend != NULL &&
        battmonitor_backend->ops != NULL &&
        battmonitor_backend->ops->get_mavlink_fault_bitmask != NULL) {
        return battmonitor_backend->ops->get_mavlink_fault_bitmask(battmonitor_backend);
    }

    return 0;
}

// amps: current (A)
// dt_us: time between samples (micro-seconds)
static inline float battmonitor_backend_calculate_mah(float amps, float dt_us) {
    return (float) (amps * dt_us * AUS_TO_MAH);
}

/*------------------------------------test------------------------------------*/

#ifdef __cplusplus
}
#endif



